#include "lib.h"

#include "rdgame.h"
#include "rdconn.h"
#include "shue.h"
#include "rdhand.h"
#include "cardmgr.h"
#include "rdranks.h"
#include "rdsaved.h"

#define REDDOG_NUMDECKS	2		// num decks in shue

enum Outcomes
{
	O_PLAYER_WON,
	O_PLAYER_LOST,
	O_PUSH
};

static CardMgr stdMgr;

#if (_DEBUG && DEBUGHANDS)
int8 getDebugCard()
{
	static uint8 cards[] = {
		0x42, 0x24, 0x13,	// 1 - 5:1
		0x18, 0x3b, 0x29,	// 2 - 4:1
		0x1e, 0x4a, 0x3c,	// 3 - 2:1
		0x3c, 0x25, 0x19,	// 4-11 Even
		0x48, 0x17,			// Cons Tie
		0x23, 0x13, 0x49,	// Pair Tie
		0x2b, 0x1b, 0x4b,	// 3 of a kind 11:1
		0x12, 0x3e, 0x42	// Loser
	};
	static int cardIndex=-1;

	if (cardIndex >= sizeof(cards)-1)
		cardIndex = 0;
	else
		++cardIndex;

	return (cards[cardIndex]);
}
#endif

RedDogGame::RedDogGame(RedDogConnection* conn, int32 bet)
{
	this->conn = conn;
	this->bet = bet;
	this->raised = false;

	shue = (Shue *)0;
	player = new RedDogHand(&stdMgr);
}

RedDogGame::RedDogGame(RedDogConnection* conn, RedDogSavedGame* saved)
{
	this->conn = conn;
	this->raised = false;

	// everything else is restored in restore()
	//
	bet = 0;
	player = (RedDogHand*)0;
	shue = (Shue *)0;
		
	restore(saved);
}

RedDogGame::~RedDogGame()
{
	delete player;
	player = (RedDogHand*)0;
	delete shue;
	shue = (Shue *)0;
}

bool8
RedDogGame::deal()
{
	ASSERT(shue == (Shue*)0);

	ShueGameInfo sgi;
	sgi.gameId = GAMEID;
	sgi.machId = MACHID;
	#ifdef FORCEDSHOE 
       sgi.pid=conn->GetPID();
    #endif

	shue = new Shue(&stdMgr, REDDOG_NUMDECKS, sgi);

	if (shue && player)
	{
		enum DealEndings { NOT_DONE, PUSH, PAYOUT } gameEnded;
		int32 balance, payout = 0;

		gameEnded = NOT_DONE;

		shue->shuffle();

#if (_DEBUG && DEBUGHANDS)
		player->add(getDebugCard());
		player->add(getDebugCard());
#else
		player->add(shue->getCard());
		player->add(shue->getCard());
#endif

		// check to see if they're consecutive rank
		//  if so, this hand is a push
		if(ABS(player->getRankAt(1) - player->getRankAt(0)) == 1)
		{
			// here we have two cards that are consecutive, so
			//  this is a push!
			gameEnded = PUSH;
			payout = 1;
		}
		else if(player->getRankAt(1) == player->getRankAt(0))
		{
			// it's not a push and the cards are the same rank!
			//  deal another card
#if (_DEBUG && DEBUGHANDS)
			player->add(getDebugCard());
#else
			player->add(shue->getCard());
#endif
			// now we must check to see if this card is the same
			//  rank as the other two.  if so, pays 11 to 1.
			if((player->getRankAt(0) == player->getRankAt(1)) &&
			   (player->getRankAt(0) == player->getRankAt(2)))
			{
				gameEnded = PAYOUT;
				payout = GetPayout();
			}
			else
			{
				// it's a push

				// push if the third card isn't the same
				gameEnded = PUSH;
				payout = 1;
			}
		}

		if (conn->trackDeal(player, (uint8)gameEnded, O_PLAYER_WON,
			payout * bet, &balance))
		{
			conn->msgStart();
			conn->msgAdd((uint8)'D');
			conn->msgAdd((uint8)(player->getCount() & 0xff));
			conn->msgReleaseBuf(player->out(conn->msgGetBuf()));
			conn->msgAdd((uint8)(gameEnded & 0xff));
			conn->msgAdd(payout);
			conn->msgAdd(balance);
			conn->msgEnd();

			if (gameEnded != NOT_DONE)
				conn->gameDone();		// deletes "this"
			else
				conn->trackSaveGame();

			return true;
		}
		else
		{
			delete shue;
			shue = (Shue *)0;
			player->removeLastCards(player->getCount());
		}
	}

	return false;
}

bool8 
RedDogGame::canRaise()
{
	return !(this->raised);
}

bool8
RedDogGame::raise(int32 raisebet, int32 balance)
{
	if (shue && player)
	{
		bool8 between;
		int32 payout;

		// the user has now said if they do or don't want to riase.
		if (raisebet > 0)
		{
			raised = true;
			bet += raisebet;
		}

		// now drop the third card down and pay them off if need be
#if (_DEBUG && DEBUGHANDS)
		player->add(getDebugCard());
#else
		player->add(shue->getCard());
#endif

		between = false;
		payout = 0;

		// check to see if the card is between the first two
		if((player->getRankAt(2) > player->getRankAt(0)) &&
		   (player->getRankAt(2) < player->getRankAt(1)))
		{
			// we're between!  SWEET!
			between = true;
		}
		else if((player->getRankAt(2) < player->getRankAt(0)) &&
			   	(player->getRankAt(2) > player->getRankAt(1)))
		{
			// we're between!  SWEET!
			between = true;
		}

		if(between)
		{
			// we're between, so i reckon we'll give them our money...
			//  *sigh*
			payout = GetPayout();

			DEBUGMSG(("you won! paying out %d to 1.\n", payout));
			DEBUGMSG(("previous balance: %d\nnew balance:%d",
				balance, balance + bet * payout));
		}
		else
		{
			DEBUGMSG(("you lost.\n"));
			DEBUGMSG(("you don't get your bet back.\n"));
		}

		if (conn->trackGameDone(
			player->getCardAt(2),
			raised,
			between ? O_PLAYER_WON : O_PLAYER_LOST,
			payout * bet,
			&balance))
		{
			conn->msgStart();
			conn->msgAdd((uint8)'F');
			conn->msgReleaseBuf(player->outCard(2, conn->msgGetBuf()));
			conn->msgAdd(payout);
			conn->msgAdd(balance);
			conn->msgEnd();

			conn->gameDone();		// deletes "this"

			return true;
		}
		else
		{
			shue->pushBackNCards(1);

			if (raised)
			{
				bet -= raisebet;
				raised = false;
			}

			// the caller tells the client about the failure
		}
	}
	return false;
}

void
RedDogGame::save(RedDogSavedGame* saved)
{
	shue->save(&saved->shue);
	// everything is already stored in the database (no need to store the shue)
	//
}

void
RedDogGame::restore(RedDogSavedGame* saved)
{
	bet = saved->bet;

	player = new RedDogHand(&stdMgr);
	player->restore(saved->playerCards, 2);

	ShueGameInfo sgi;
	sgi.gameId = GAMEID;
	sgi.machId = MACHID;
	#ifdef FORCEDSHOE 
       sgi.pid=conn->GetPID();
    #endif

	shue = new Shue(&stdMgr, REDDOG_NUMDECKS, sgi);
	shue->restore(&saved->shue);

	//
	// At this point the server is *exactly* the same as when the game was
	// saved:  all automatic playing had already completed and the game
	// was idle.
	//
}

void
RedDogGame::send()
{
	//
	// This message is contained within another message
	// (don't call msgStart()/msgEnd()).
	//
	conn->msgAdd(bet);
	conn->msgAdd((uint8)(player->getCount() & 0xff));
	conn->msgReleaseBuf(player->out(conn->msgGetBuf()));
}

int32 RedDogGame::GetPayout()
{
	// calculate the spread
	int32 spread = ABS( player->getRankAt(1) - player->getRankAt(0) );

	if(0 == spread)
	{
		return 12;
	}
	else if(2 == spread)
	{
		return 6;
	}
	else if(3 == spread)
	{
		return 5; 
	}
	else if(4 == spread)
	{
		return 3;
	}
	else if(5 <= spread && 12 >= spread)
	{
		return 2;
	}
	else 
	{
		return 0;
	}

}
